﻿using System;
using System.Linq;
using Bots.DungeonBuddy.Attributes;
using Bots.DungeonBuddy.Helpers;
using CommonBehaviors.Actions;
using Styx.Helpers;
using Styx.Logic;
using Styx.Logic.Pathing;
using Styx.WoWInternals;
using TreeSharp;
using Styx;
using Styx.WoWInternals.WoWObjects;
using System.Collections.Generic;
using Action = TreeSharp.Action;

namespace Bots.DungeonBuddy.Dungeons.Cataclysm
{
    public class HeroicEndTime : Dungeon
    {
        #region Overrides of Dungeon

        /// <summary> The Map Id of this dungeon. This is the unique id for dungeons thats used to determine which dungeon, 
        /// the script belongs to </summary>
        /// <value>The map identifier.</value>
        public override uint DungeonId
        {
            get { return 435; }
        }

        public override WoWPoint Entrance
        {
            get
            {
                return new WoWPoint(-8298.268f, -4456.12f, -208.95f);
            }
        }

        /// <summary>
        /// IncludeTargetsFilter is used to add units to the targeting list. If you want to include a mob thats usually removed by the default
        /// filters, you shall use that.
        /// </summary>
        /// <param name="incomingunits">Units passed into the method</param>
        /// <param name="outgoingunits">Units passed to the targeting class</param>
        public override void IncludeTargetsFilter(List<WoWObject> incomingunits, HashSet<WoWObject> outgoingunits)
        {
            foreach (WoWObject obj in incomingunits)
            {
                // For the Rom'ogg boss fight
                if (obj.Entry == ChainsOfWoe)
                {
                    outgoingunits.Add(obj);
                }
            }
        }

        /// <summary>
        /// RemoveTargetsFilter is used to remove units thats not wanted in target list. Like immune mobs etc.
        /// </summary>
        /// <param name="units"></param>
        public override void RemoveTargetsFilter(List<WoWObject> units)
        {
            units.RemoveAll(o =>
                                {
                                    var unit = o as WoWUnit;

                                    if (unit == null)
                                        return false;

                                    if (unit.HasAura("Shadow of Obsidius"))
                                        return true;

                                    return false;
                                });
        }


        /// <summary>
        /// WeighTargetsFilter is used to weight units in the targeting list. If you want to give priority to a certain npc, you should
        /// use this method. 
        /// </summary>
        /// <param name="units"></param>
        public override void WeighTargetsFilter(List<Targeting.TargetPriority> units)
        {
            foreach (var t in units)
            {
                var prioObject = t.Object;

                //We should prio Chains of Woe for Rom'ogg fight
                if (prioObject.Entry == ChainsOfWoe)
                {
                    t.Score += 400;
                }
            }
        }

        public List<WoWUnit> BainesTotem
        {
            get
            {
                return ObjectManager.GetObjectsOfType<WoWUnit>()
                                            .Where(u => !u.Dead && u.Entry == 54434)
                                            .OrderBy(u => u.Distance).ToList();
            }
        }

        public List<WoWDynamicObject> AoESpellEnd
        {
            get
            {
                return ObjectManager.GetObjectsOfType<WoWDynamicObject>()
                                            .Where(u => u.Entry == 101984)
                                            .OrderBy(u => u.Distance).ToList();
            }
        }

        #endregion

        #region Encounter Handlers

        const uint ChainsOfWoe = 40447;


        /// <summary>
        /// Using 0 as BossEntry will make that composite the main logic for the dungeon and it will be called in every tick
        /// You can only have one main logic for a dungeon
        /// The context of the main composite is all units around as List<WoWUnit/>
        /// </summary>
        /// <returns></returns>
        [EncounterHandler(0)]
        public Composite RootLogic()
        {
            return
                new PrioritySelector(
                    ScriptHelpers.CreateTeleporterLogic(209441),
                    // ScriptHelpers.CreateRunAwayFromAura("Lava Drool"),
                    ScriptHelpers.CreateRunAwayFromBad(ctx => true,10f, 101984)
                    );
        }

        /// <summary>
        /// BossEntry is the Entry of the boss unit. (WoWUnit.Entry)
        /// BossName is optional. Its there just to make it easier to find which boss that composite belongs to.
        /// The context of the encounter composites is the Boss as WoWUnit
        /// </summary>
        /// <returns></returns>
        [EncounterHandler(54431, "Echo of Baine")]
        public Composite EchoOfBaineFightLogic()
        {
            //const int theSkullCracker = 75543;
            return
                new PrioritySelector(
                //Lava is more important then Totem, lets get out of it first
                    new Decorator(
                        ctx => ObjectManager.Me.HasAura(("Magma")),
                        new Action(ctx =>
                        {

                            Logger.Write("[Echo of Baine Encounter] In The Lava, Moving to Closest Player without Lava Aura");

                            List<WoWUnit> playerAuras = ObjectManager.GetObjectsOfType<WoWUnit>()
                                            .Where(u => !u.Dead && !u.HasAura("Magma"))
                                            .OrderBy(u => u.Distance).ToList();

                            if (playerAuras.Count > 0)
                            {
                                return Navigator.GetRunStatusFromMoveResult(Navigator.MoveTo(playerAuras[0].Location));
                            }
                            Logger.Write("[Echo of Baine Encounter] All Players have Lava. Following Tank Until No Magam Present");

                            WoWPlayer tankList = ScriptHelpers.Tank ?? ObjectManager.Me.PartyMember1;

                            return Navigator.GetRunStatusFromMoveResult(Navigator.MoveTo(tankList.Location));
                        })
                            ),
                    new Decorator(
                        ctx => BainesTotem.Count > 0 && ObjectManager.Me.Role != WoWPartyMember.GroupRole.Healer && BainesTotem[0].Distance >= BainesTotem[0].InteractRange,
                        new Action(ctx =>
                                       {
                                           Logger.Write("[Echo of Baine Encounter] Totem Detected Moving to Throw");

                                           return Navigator.GetRunStatusFromMoveResult(Navigator.MoveTo(BainesTotem[0].Location));
                                       })
                            ),

                    new Decorator(
                        ctx => BainesTotem.Count > 0 && ObjectManager.Me.Role != WoWPartyMember.GroupRole.Healer && BainesTotem[0].Distance >= BainesTotem[0].InteractRange && ObjectManager.Me.IsMoving,
                        new Action(ctx => { WoWMovement.MoveStop(); return RunStatus.Success; })
                            ),

                   new Decorator(
                        ctx => BainesTotem.Count > 0 && !ScriptHelpers.Healer.IsMe && BainesTotem[0].Distance < BainesTotem[0].InteractRange && !ObjectManager.Me.IsMoving,
                        new Action(ctx =>
                                       {
                                           Logger.Write("[Echo of Baine Encounter] Throwing Totem");

                                           WoWUnit bossList = ObjectManager.GetObjectsOfType<WoWUnit>().Where(u => !u.Dead).OrderBy(u => u.Distance).FirstOrDefault();

                                           BainesTotem[0].Interact();

                                           if (bossList != null)
                                               Styx.Logic.Combat.LegacySpellManager.ClickRemoteLocation(bossList.Location);

                                           return RunStatus.Success;
                                       })
                            ));
        }

        [EncounterHandler(54432, "Murozond")]
        public Composite MurozondFightLogic()
        {
            return
                new PrioritySelector(

                    new Decorator(
                        ctx => AoESpellEnd.Count > 0 && ObjectManager.Me.Location.Distance(AoESpellEnd[0].Location) < AoESpellEnd[0].Radius + 4,
                        new Action(ctx =>
                        {
                            if (ObjectManager.Me.IsMoving)
                                return RunStatus.Success;

                            Logger.Write("[Murozond Encounter] Moving out of AoE");

                            var rnd = new Random();
                            float randomRotation = WoWMathHelper.DegreesToRadians(rnd.Next(1, 360));

                            WoWPoint movePoint = WoWMathHelper.GetPointAt(ObjectManager.Me.Location,
                                                             ((AoESpellEnd[0].Radius + 4) -
                                                             ObjectManager.Me.Location.Distance(AoESpellEnd[0].Location)), randomRotation, 0);

                            return Navigator.GetRunStatusFromMoveResult(Navigator.MoveTo(movePoint));
                        })
                            ));
        }

        #endregion
    }
}
